﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using org.mariuszgromada.math.mxparser;
using System;

namespace Utility.DataSystem
{

	[Serializable]
	public class ActionParam
	{
		public string str = "";
		public bool isExpression = true;
	}

	[Serializable]
	public class EventActionSettingObject : SettingObject<EventActionSetting>
	{
		[Serializable]
		public class Action
		{
			public string actionName;
			public ActionParam[] parameters = new ActionParam[0];
		}

		[Serializable]
		public class VariableParams
		{
			public ActionParam[] parameters = new ActionParam[0];
		}

		public string eventName;
		public List<string> variables = new List<string> ();
		public List<VariableParams> variableParams = new List<VariableParams> ();

		public List<string> conditions = new List<string> ();
		public List<Action> actions = new List<Action> ();

		public override bool IsValid
		{
			get
			{
				if (variableParams.Count != variables.Count)
				{
					variableParams.Clear ();
					for (int i = 0; i < variables.Count; i++)
					{
						variableParams.Add (new VariableParams ());
					}
				}

				//event
				if (!FieldCenter.ContainsEvent (eventName))
				{
					return false;
				}
				//variables
				for (int i = 0; i < variables.Count; i++)
				{
					if (!FieldCenter.ContainsField (variables[i]))
					{
						return false;
					}
					foreach (var param in variableParams[i].parameters)
					{
						if (string.IsNullOrWhiteSpace (param.str))
						{
							return false;
						}
					}
				}
				//conditions
				foreach (string c in conditions)
				{
					if (string.IsNullOrWhiteSpace (c))
					{
						return false;
					}
				}
				//actions
				foreach (var a in actions)
				{
					//action
					if (!FieldCenter.ContainsAction (a.actionName))
					{
						return false;
					}
					//params
					foreach (var param in a.parameters)
					{
						if (string.IsNullOrWhiteSpace (param.str))
						{
							return false;
						}
					}
				}
				return true;
			}
		}
	}

	public class EventActionSetting : Setting<EventActionSettingObject>, IFieldEventListener
	{
		class Info
		{
			public string key;
			public object[] expressions;//obj is string or Expression
			public DataValue[] paramValues;
		}

		//argument-field key
		Dictionary<Argument, Info> argumentDict = new Dictionary<Argument, Info> ();
		List<Expression> conditions = new List<Expression> ();
		List<Info> actions = new List<Info> ();

		public string Name { get; set; } = "EventAction";

		protected override void Init ()
		{
			base.Init ();
			//arguments
			argumentDict.Clear ();
			char varName = 'a';
			for (int i = 0; i < SettingObject.variables.Count; i++)
			{
				var field = SettingObject.variables[i];
				var param = SettingObject.variableParams?.TryGet (i)?.parameters;
				if (FieldCenter.ContainsField (field))
				{
					Argument arg = new Argument (varName.ToString (), 0);
					var info = new Info ();
					info.key = field;
					if (param != null && param.Length > 0)
					{
						info.expressions = new object[param.Length];
						info.paramValues = new DataValue[param.Length];
						for (int p = 0; p < param.Length; p++)
						{
							info.expressions[p] = param[p].isExpression ? new Expression (param[p].str, argumentDict.Keys.ToArray ()) : (object)param[p].str;
						}
					}
					argumentDict.Add (arg, info);
				}
				varName++;
				if (varName == 'e')
				{
					varName++;
				}
			}
			Argument[] args = argumentDict.Keys.ToArray ();
			//expressions-condition
			conditions.Clear ();
			foreach (var str in SettingObject.conditions)
			{
				if (!string.IsNullOrEmpty (str))
				{
					Expression e = new Expression (str, args);
					conditions.Add (e);
				}
			}
			//expression-function
			actions.Clear ();
			foreach (var a in SettingObject.actions)
			{
				var info = new Info ();
				info.key = a.actionName;
				var param = a.parameters;
				if (param != null && param.Length > 0)
				{
					info.expressions = new object[param.Length];
					info.paramValues = new DataValue[param.Length];
					for (int p = 0; p < param.Length; p++)
					{
						info.expressions[p] = param[p].isExpression ? new Expression (param[p].str, argumentDict.Keys.ToArray ()) : (object)param[p].str;
					}
				}
				actions.Add (info);
			}
		}

		void IFieldEventListener.OnEvent (IDataFieldContext context, FieldEvent e, out bool interested)
		{
			//check key
			if (e.Setting.Key != SettingObject.eventName)
			{
				interested = false;
				return;
			}

			var msg = FieldCenter.GetDebugMsg (e);
			interested = true;
			//update arguments
			foreach (var pair in argumentDict)
			{
				var expressions = pair.Value.expressions;
				var parameters = pair.Value.paramValues;
				if (expressions != null && expressions.Length > 0)
				{
					for (int x = 0; x < expressions.Length; x++)
					{
						DataValue data = default;

						var p = expressions[x];
						if (p is Expression exp)
						{
							var value_Argument = exp.calculate ();
							Debug.Assert (double.IsNaN (value_Argument) == false, $"SettingObject.eventName：{SettingObject.eventName} Effector calculate result IsNaN: Please check expression");

							data = new DataValue (value_Argument);
						}
						else if (p is string str)
						{
							data = new DataValue (str);
						}

						parameters[x] = data;
					}
				}
				var valueData = context.GetValue (pair.Value.key, true, parameters);
				if (valueData.IsNumber)
				{
					pair.Key.setArgumentValue ((double)valueData.AsNumber ());
				}
				else if (valueData.IsBool)
				{
					pair.Key.setArgumentValue (Convert.ToDouble (valueData.AsBool ()));
				}
				else if (valueData.IsEnum)
				{
					pair.Key.setArgumentValue (Convert.ToInt32 (valueData.AsEnum ()));
				}
				msg?.EffectorVariable (pair.Value.key, valueData);
			}

			//check conditions
			for (int i = 0; i < conditions.Count; i++)
			{
				bool pass = conditions[i].calculate () > 0;
				msg?.EffectorCondition (i, pass);
				if (!pass)
				{
					return;
				}
			}
			//return value
			for (int i = 0; i < actions.Count; i++)
			{
				string action = actions[i].key;
				var parameters = actions[i].paramValues;
				var expressions = actions[i].expressions;
				if (expressions != null)
				{
					for (int x = 0; x < expressions.Length; x++)
					{
						DataValue data = default;

						var p = expressions[x];
						if (p is Expression exp)
						{
							var value_Return = exp.calculate ();
							Debug.Assert (double.IsNaN (value_Return) == false, $"SettingObject.eventName：{SettingObject.eventName} Effector calculate result IsNaN: Please check expression");

							data = new DataValue (value_Return);
						}
						else if (p is string str)
						{
							data = new DataValue (str);
						}

						parameters[x] = data;
					}
				}

				context.PerformAction (action, parameters);
			}
		}
	}
}